46. URL Encode/Decode

Note

The below information is extensively based in information taken from the PowerShell® Notes for Professionals book. I plan to extend this information based on my day to day usage of the language.

46.1: Encode Query String with [System.Web.HttpUtility]::UrlEncode()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$scheme = 'https'
$url_format = '{0}://example.vertigion.com/foos?{1}'
$qs_data = @{
  'foo1'                  = 'bar1'
  'foo2'                  = 'complex;/?:@&=+$, bar''"'
  'complex;/?:@&=+$, foo''"' = 'bar2'
}
[System.Collections.ArrayList] $qs_array = @()
foreach ($qs in $qs_data.GetEnumerator()) 
{
  $qs_key = [System.Web.HttpUtility]::UrlEncode($qs.Name)
  $qs_value = [System.Web.HttpUtility]::UrlEncode($qs.Value)
  $null = $qs_array.Add("${qs_key}=${qs_value}")
}

$url = $url_format -f @([uri]::"UriScheme${scheme}", ($qs_array -join '&'))

With [System.Web.HttpUtility]::UrlEncode(), you will notice that spaces are turned into plus signs (+) instead of %20:

1
2
3
https://example.vertigion.com/foos?
foo2=complex%3b%2f%3f%3a%40%26%3d%2b%24%2c+bar%27%22&
complex%3b%2f%3f%3a%40%26%3d%2b%24%2c+foo%27%22=bar2&foo1=bar1

46.2: Quick Start: Encoding

1
2
$url1 = [uri]::EscapeDataString("http://test.com?test=my value")
# url1: http%3A%2F%2Ftest.com%3Ftest%3Dmy%20value
1
2
$url2 = [uri]::EscapeUriString("http://test.com?test=my value")
# url2: http://test.com?test=my%20value
1
2
3
# HttpUtility requires at least .NET 1.1 to be installed.
$url3 = [System.Web.HttpUtility]::UrlEncode("http://test.com?test=my value")
# url3: http%3a%2f%2ftest.com%3ftest%3dmy+value

Note: More info on HTTPUtility.

46.3: Quick Start: Decoding

Note: these examples use the variables created in the Quick Start: Encoding above.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# url1: http%3A%2F%2Ftest.com%3Ftest%3Dmy%20value
[uri]::UnescapeDataString($url1)
# Returns: http://test.com?test=my value

# url2: http://test.com?test=my%20value
[uri]::UnescapeDataString($url2)
# Returns: http://test.com?test=my value
# url3: http%3a%2f%2ftest.com%3ftest%3dmy+value
[uri]::UnescapeDataString($url3)
# Returns: http://test.com?test=my+value

# Note: There is no `[uri]::UnescapeUriString()`;
# which makes sense since the `[uri]::UnescapeDataString()`
# function handles everything it would handle plus more.

# HttpUtility requires at least .NET 1.1 to be installed.
# url1: http%3A%2F%2Ftest.com%3Ftest%3Dmy%20value
[System.Web.HttpUtility]::UrlDecode($url1)
# Returns: http://test.com?test=my value

# HttpUtility requires at least .NET 1.1 to be installed.
# url2: http://test.com?test=my%20value
[System.Web.HttpUtility]::UrlDecode($url2)
# Returns: http://test.com?test=my value

# HttpUtility requires at least .NET 1.1 to be installed.
# url3: http%3a%2f%2ftest.com%3ftest%3dmy+value
[System.Web.HttpUtility]::UrlDecode($url3)
# Returns: http://test.com?test=my value

Note: More info on HTTPUtility.

46.4: Encode Query String with [uri]::EscapeDataString()

1
2
3
4
5
6
7
$scheme = 'https'
$url_format = '{0}://example.vertigion.com/foos?{1}'
$qs_data = @{
  'foo1'='bar1';
  'foo2'= 'complex;/?:@&=+$, bar''"';
  'complex;/?:@&=+$, foo''"'='bar2';
}
1
2
3
4
5
6
7
8
[System.Collections.ArrayList] $qs_array = @()
foreach ($qs in $qs_data.GetEnumerator()) {
  $qs_key = [uri]::EscapeDataString($qs.Name)
  $qs_value = [uri]::EscapeDataString($qs.Value)
  $qs_array.Add("${qs_key}=${qs_value}") | Out-Null
}

$url = $url_format -f @([uri]::"UriScheme${scheme}", ($qs_array - join '&'))

With [uri]::EscapeDataString(), you will notice that the apostrophe (') was not encoded:

1
2
3
https://example.vertigion.com/foos?
foo2=complex%3B%2F%3F%3A%40%26%3D%2B%24%2C%20bar'%22&
complex%3B%2F%3F%3A%40%26%3D%2B%24%2C%20foo'%22=bar2&foo1=bar1

46.5: Decode URL with [uri]::UnescapeDataString()

Encoded with [uri]::EscapeDataString()

First, we'll decode the URL and Query String encoded with [uri]::EscapeDataString() in the above example:

1
2
3
https://example.vertigion.com/foos?
foo2=complex%3B%2F%3F%3A%40%26%3D%2B%24%2C%20bar'%22&
complex%3B%2F%3F%3A%40%26%3D%2B%24%2C%20foo'%22=bar2&foo1=bar1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$url =
'https://example.vertigion.com/foos?foo2=complex%3B%2F%3F%3A%40%26%3D%2B%24%2C%20bar''%22&complex%3B%2F%3F%3A%40%26%3D%2B%24%2C%20foo''%22=bar2&foo1=bar1'
$url_parts_regex = '^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?' # See Remarks

if ($url -match $url_parts_regex) {
  $url_parts = @{
    'Scheme' = $Matches[ 2 ];
    'Server' = $Matches[ 4 ];
    'Path' = $Matches[ 5 ];
    'QueryString' = $Matches[ 7 ];
    'QueryStringParts' = @{}
  }

  foreach ($qs in $query_string.Split('&')) {
    $qs_key, $qs_value = $qs.Split('=')
    $url_parts.QueryStringParts.Add(
      [uri]::UnescapeDataString($qs_key),
      [uri]::UnescapeDataString($qs_value)
    ) | Out-Null
  }
} else {
  Throw [System.Management.Automation.ParameterBindingException] "Invalid URL Supplied"
}

This gives you back [hashtable]$url_parts; which equals ( Note: the spaces in the complex parts are spaces ):

1
 $url_parts
1
2
3
4
5
6
7
8
9
Name Value
---- -----
Scheme https
Path /foos
Server example.vertigion.com
QueryString
foo2=complex%3B%2F%3F%3A% 40 % 26 %3D%2B% 24 %2C%20bar'%22&complex%3B%2F%3F%3A%40%26%3D%2B%24%2C%20foo'%
22 =bar2&foo1=bar1
QueryStringParts {foo2, complex;/?:@&=+$, foo'", foo1}
1
 $url_parts.QueryStringParts
1
2
3
4
5
Name Value
---- -----
foo2 complex;/?:@&=+$, bar'"
complex;/?:@&=+$, foo'" bar2
foo1 bar1

Encoded with [System.Web.HttpUtility]::UrlEncode()

Now, we'll decode the URL and Query String encoded with [System.Web.HttpUtility]::UrlEncode() in the above

example:

1
https://example.vertigion.com/foos?
1
2
foo2=complex%3b%2f%3f%3a%40%26%3d%2b%24%2c+bar%27%22&
complex%3b%2f%3f%3a%40%26%3d%2b%24%2c+foo%27%22=bar2&foo1=bar1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
$url =
'https://example.vertigion.com/foos?foo2=complex%3b%2f%3f%3a%40%26%3d%2b%24%2c+bar%27%22&complex%3b%2f%3f%3a%40%26%3d%2b%24%2c+foo%27%22=bar2&foo1=bar1'
$url_parts_regex = '^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?' # See Remarks
if ($url -match $url_parts_regex) {
  $url_parts = @{
    'Scheme' = $Matches[2];
    'Server' = $Matches[4];
    'Path' = $Matches[5];
    'QueryString' = $Matches[7];
    'QueryStringParts' = @{}
  }

  foreach ($qs in $query_string.Split('&')) {
    $qs_key, $qs_value = $qs.Split('=')
    $url_parts.QueryStringParts.Add(
      [uri]::UnescapeDataString($qs_key),
      [uri]::UnescapeDataString($qs_value)
    ) | Out-Null
  }
} else {
  Throw [System.Management.Automation.ParameterBindingException] "Invalid URL Supplied"
}

This gives you back [hashtable]$url_parts, which equals ( Note: the spaces in the complex parts are plus signs (+) in the first part and spaces in the second part):

1
 $url_parts
1
2
3
4
5
6
7
8
9
Name Value
---- -----
Scheme https
Path /foos
Server example.vertigion.com
QueryString
foo2=complex%3b%2f%3f%3a% 40 % 26 %3d%2b% 24 %2c+bar% 27 % 22 &complex%3b%2f%3f%3a% 40 % 26 %3d%2b% 24 %2c+foo% 27 %
22 =bar2&foo1=bar1
QueryStringParts {foo2, complex;/?:@&=+$, foo'", foo1}
1
 $url_parts.QueryStringParts
1
2
3
4
5
Name Value
---- -----
foo2 complex;/?:@&=+$, bar'"
complex;/?:@&=+$, foo'" bar2
foo1 bar1

46.6: Decode URL with [System.Web.HttpUtility]::UrlDecode()

Encoded with [uri]::EscapeDataString()

First, we'll decode the URL and Query String encoded with [uri]::EscapeDataString() in the above example:

1
2
3
https://example.vertigion.com/foos?
foo2=complex%3B%2F%3F%3A%40%26%3D%2B%24%2C%20bar'%22&
complex%3B%2F%3F%3A%40%26%3D%2B%24%2C%20foo'%22=bar2&foo1=bar1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
$url =
'https://example.vertigion.com/foos?foo2=complex%3B%2F%3F%3A%40%26%3D%2B%24%2C%20bar''%22&complex%3B%2F%3F%3A%40%26%3D%2B%24%2C%20foo''%22=bar2&foo1=bar1'
$url_parts_regex = '^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?' # See Remarks
if ($url -match $url_parts_regex) {
  $url_parts = @{
    'Scheme' = $Matches[2];
    'Server' = $Matches[4];
    'Path' = $Matches[5];
    'QueryString' = $Matches[7];
    'QueryStringParts' = @{}
  }

  foreach ($qs in $query_string.Split('&')) {
    $qs_key, $qs_value = $qs.Split('=')
    $url_parts.QueryStringParts.Add(
      [System.Web.HttpUtility]::UrlDecode($qs_key),
      [System.Web.HttpUtility]::UrlDecode($qs_value)
    ) | Out-Null
  }
} else {
  Throw [System.Management.Automation.ParameterBindingException] "Invalid URL Supplied"
}

This gives you back [hashtable]$url_parts; which equals ( Note: the spaces in the complex parts are spaces ):

1
 $url_parts
1
2
3
4
5
6
7
8
9
Name Value
---- -----
Scheme https
Path /foos
Server example.vertigion.com
QueryString
foo2=complex%3B%2F%3F%3A% 40 % 26 %3D%2B% 24 %2C%20bar'%22&complex%3B%2F%3F%3A%40%26%3D%2B%24%2C%20foo'%
22 =bar2&foo1=bar1
QueryStringParts {foo2, complex;/?:@&=+$, foo'", foo1}
1
 $url_parts.QueryStringParts
1
2
3
4
5
Name Value
---- -----
foo2 complex;/?:@&=+$, bar'"
complex;/?:@&=+$, foo'" bar2
foo1 bar1

Encoded with [System.Web.HttpUtility]::UrlEncode()

Now, we'll decode the URL and Query String encoded with [System.Web.HttpUtility]::UrlEncode() in the above

example:

1
https://example.vertigion.com/foos?
1
2
foo2=complex%3b%2f%3f%3a%40%26%3d%2b%24%2c+bar%27%22&
complex%3b%2f%3f%3a%40%26%3d%2b%24%2c+foo%27%22=bar2&foo1=bar1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
$url =
'https://example.vertigion.com/foos?foo2=complex%3b%2f%3f%3a%40%26%3d%2b%24%2c+bar%27%22&complex%3b%2f%3f%3a%40%26%3d%2b%24%2c+foo%27%22=bar2&foo1=bar1'
$url_parts_regex = '^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?' # See Remarks
if ($url -match $url_parts_regex) {
  $url_parts = @{
    'Scheme' = $Matches[2];
    'Server' = $Matches[4];
    'Path' = $Matches[5];
    'QueryString' = $Matches[7];
    'QueryStringParts' = @{}
  }

  foreach ($qs in $query_string.Split('&')) {
    $qs_key, $qs_value = $qs.Split('=')
    $url_parts.QueryStringParts.Add(
      [System.Web.HttpUtility]::UrlDecode($qs_key),
      [System.Web.HttpUtility]::UrlDecode($qs_value)
    ) | Out-Null
  }
} else {
  Throw [System.Management.Automation.ParameterBindingException] "Invalid URL Supplied"
}

This gives you back [hashtable]$url_parts; which equals ( Note: the spaces in the complex parts are spaces ):

1
 $url_parts
1
2
3
4
5
6
7
8
9
Name Value
---- -----
Scheme https
Path /foos
Server example.vertigion.com
QueryString
foo2=complex%3b%2f%3f%3a% 40 % 26 %3d%2b% 24 %2c+bar% 27 % 22 &complex%3b%2f%3f%3a% 40 % 26 %3d%2b% 24 %2c+foo% 27 %
22 =bar2&foo1=bar1
QueryStringParts {foo2, complex;/?:@&=+$, foo'", foo1}
1
 $url_parts.QueryStringParts
1
2
3
4
5
Name Value
---- -----
foo2 complex;/?:@&=+$, bar'"
complex;/?:@&=+$, foo'" bar2
foo1 bar1